package {
	import flash.events.Event;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
    import mx.skins.halo.ApplicationBackground;
    import org.flixel.*;
	import org.flixel.plugin.photonstorm.FlxBar;
	import XML;
	import Resources;
	import cse481d.Logger;
  
    // Test State to check object construction
    public class Level extends FlxState {
        public function Level(levelData:XML, number:int, data:GameProgress, levelSelection:LevelSelect, bgMusic:FlxSound) {
            super();
			levelInfo = levelData;
            levelSelect = levelSelection;
			levelNum = number;
			gameInfo = data;
			loaded = false;
			drawingInk = false;
			inkPlatforms = new FlxGroup();
			
			logger = Logger.initialize(GID, NAME, SKEY, CID, { "isdebug":true } );
			soundtrack = bgMusic;
        }
        // Logging
		private var logger:Logger;
		
		private static const NAME:String = "ink";
		private static const GID:uint = 38;
		private static const SKEY:String = "33b7a1dfb366e9f705f6d9d39efd13a0";
		// 71 for internal testing
		// 3 for First Release Testing: Albino Black Sheep
		// 6 for newgrounds, 15 for newgrounds v2
		// 8 for Armor Games
		// 9 for Addictinggames.com
		// 10 for Kongregate!
		// 15 for Kongregate Logging Fix
		private static const CID:int = 15;
		
		
        // Locals
        private var platforms:FlxGroup;
		private var inkPlatforms:FlxGroup;
        private var startDoor:Door;
        private var finishDoor:Door;        
        private var player:Player;
		
		private var texts:FlxGroup;
		private var title:FlxText;
		private var inkCount:FlxText;
		private var resetCount:Number = 0;
		private var levelTime:FlxText;
		private var notification:FlxText;
		private var inkMeter:FlxBar;
		
        private var switches:FlxGroup;
		private var switchesList:Vector.<SwitchButton>;
        private var lockedDoors:FlxGroup;
		private var lockedDoorsList:Vector.<LockedDoor>;
        private var disappearingPlatforms:FlxGroup;
		private var disappearingPlatformsList:Vector.<DisappearingPlatform>;
        private var movingPlatforms:FlxGroup;
		private var movingPlatformsList:Vector.<MovingPlatform>;
		private var balls:FlxGroup;
		private var ballLaunchers:FlxGroup;
		private var ballTargets:FlxGroup;
		
		
		
		private var heatMapRects:FlxGroup;
		
		// One collision group to encompass everything that collides the same way
		private var playerCollisionGroup:FlxGroup;
		
		// Everything that just collides with the player with no additional callbacks
		// can be condensed into this one group.
		private var standardCollisionGroup:FlxGroup;
        
        // Level selection screen object
        private var levelSelect:LevelSelect;
        
        // Background image
        private var background:Backdrop;
		
		// Keep track of whether or not we're drawing ink
		private var drawingInk:Boolean;
		private var curInk:InkPlatform;
		
		// The XML object passed from LevelSelect that contains
		// the level.
		private var levelInfo:XML;
		
		// Keep track of which level we're on.
		private var levelNum:int;
		// A metrics object that keeps track of everything.
		private var gameInfo:GameProgress;
		// Set to true once we're ready to begin updating
		private var loaded:Boolean;
		
		// Timer to time player at each level
		private var elapsedTime:Number; 
		
		// A ref to our background sound
		private var soundtrack:FlxSound;
        
        private var buttons:Array;
        private var resetButton:FlxButton;
        private var levelSelectButton:FlxButton;
		private var muteButton:FlxButton;
		
		// Keep track of how many bubbles we've sent so we know which one we're on
		private var numBubbles:int;
		
        override public function create():void {
            //FlxG.bgColor = 0xffFFFFCC;
            background = new Backdrop(0, 0);
            add(background);
			FlxG.mouse.hide();

            loadLevel(levelInfo, true);
			numBubbles = 0;
        }
        
        // Add Collision, Ink drawing, goal state, etc
        override public function update():void {
            super.update();
			
            if ( loaded ) {
				elapsedTime += FlxG.elapsed;
				levelTime.text = "Time Elapsed: " + FlxU.formatTime(elapsedTime);
				
				updateTutorialBubbles();

				if (player.x  + player.width < 0 || player.x > FlxG.width + player.width || player.y + player.height < 0 || player.y > FlxG.height + player.height)
					resetLevel();
				
				// These could be done with callbacks.  We could get a slight performance
				// by changing it.
                for each(var switchB:SwitchButton in switchesList) {
                    // Check if player overlaps with any switches and presses the down key
                    if (player.overlaps(switchB) && (FlxG.keys.justPressed("S") || FlxG.keys.justPressed("DOWN"))) {
                        // Switch the switch on
                        switchB.toggleOn();
                        var id:int = switchB.getId();
                        for each(var lockedDoor:LockedDoor in lockedDoorsList) {
                            // Unlock the door associated with the switch
                            if (lockedDoor.getId() == id) {
                                lockedDoor.toggleLock();
                                lockedDoor.solid = lockedDoor.isLocked();
                            }
                        }
                        for each(var dp:DisappearingPlatform in disappearingPlatformsList) {
                            if (dp && dp.getId() == id) {
                                dp.toggleVisible();
                            }
                        }
                        for each(var mp:MovingPlatform in movingPlatformsList) {
                            if (mp && mp.getId() == id) {
                                mp.move();
                            }
                        }
						for each(var bl:BallLauncher in ballLaunchers.members) {
							// Launch a ball
							if (bl && bl.getId() == id) {
								bl.launch(this);
							}
						}
                    }
                }
				
            
            
				if ( player.overlaps(finishDoor) )
				{
					gameInfo.finishLevel(levelNum, player.getInkCount());
					/*
					var ls:LevelSelect = new LevelSelect();
					ls.setGameProgress(gameInfo);
					FlxG.switchState(ls);
					*/
					logger.logAction(1, "Ink Count: " + String(player.getInkCount()) + 
											"Number of Resets: " + String(resetCount) +
											"Elapsed Time: " +  FlxU.formatTime(elapsedTime) );
					
					for each( var plat:InkPlatform in inkPlatforms.members )
					{
						logger.logAction(2, { "X":String(plat.x),"Y":String(plat.y),
													"W":String(plat.width),"H":String(plat.height) } );
					}
					
					
					logger.logLevelEnd({"didwin":true});

					var ss:ScoreScreen = new ScoreScreen(levelSelect, levelNum, gameInfo, player.getInkCount(), elapsedTime);
					FlxG.switchState(ss);
				}
				
				if ( FlxG.keys.justPressed("R") )
					resetLevel();
					
				// Quit current level and progress
				if ( FlxG.keys.justPressed("ESCAPE")) {
					var ls:LevelSelect = new LevelSelect(soundtrack);
					ls.setGameProgress(gameInfo);
					FlxG.switchState(ls);
				}
				
				inkCount.text = "Ink Left: " + player.getInkCount();
				
				if (player.getInkCount() == 0) {
					notification.text = "Out of Ink!";
				}
				
				// Player collision will be separate from other object collision.
				// FlxObject.separate(..) isn't precise enough for our purposes.
				for each( var ip:InkPlatform in inkPlatforms.members )
				{
					if ( !FlxG.overlap(player, ip, collideInkPlayer) && ip.playerPresent )
					{
						ip.playerPresent = false;
						player.acceleration.y = player.GRAV_NORM;
					}
				}
				
				FlxG.collide(player, playerCollisionGroup);
				// Ball targets collide differenty with players and balls, so they have to be
				// collided separately
				FlxG.collide(player, ballTargets);
				FlxG.overlap(balls, inkPlatforms, collideInkBall);
				// See if the ball hit a switch
				FlxG.collide(balls, ballTargets, targetNotify);
				FlxG.collide(balls, playerCollisionGroup, collideFloorBall);
				// Add collision between balls and platforms, ink, etc
				FlxG.collide(balls, player);
				FlxG.collide(balls, balls);
			}
        }
		
		private function loadLevel(parser:XML, hardReset:Boolean): void
		{	
			if ( loaded )
				return;
                
			// Find all the platforms
			var numPlatforms:int = parser.platform.length();
			platforms = new FlxGroup();
			
			for (var i:int = 0; i < numPlatforms; i++ )
			{
				var x:int = parser.platform[i].@x;
				var y:int = parser.platform[i].@y;
				var w:int = parser.platform[i].@w;
				var h:int = parser.platform[i].@h;
				var g:Boolean = parser.platform[i].@g == 1;
				
				platforms.add(new Platform(x, y, w, h, true, g));
			}
			
            // Add switches and locked doors
            var numSwitches:int = parser.switchButton.length();
			switches = new FlxGroup();
			switchesList = new Vector.<SwitchButton>();
			
			for (i = 0; i < numSwitches; i++ )
			{
				x = parser.switchButton[i].@x;
				y = parser.switchButton[i].@y;
				var id:int = parser.switchButton[i].@id;
				var on:Boolean;
				parser.switchButton[i].@on == "true" ? on = true: on = false;
				var sb:SwitchButton = new SwitchButton(x, y, id, on);
				switches.add(sb);
				switchesList.push(sb);
			}
            
            var numLockedDoors:int = parser.lockedDoor.length();
			lockedDoors = new FlxGroup();
			lockedDoorsList = new Vector.<LockedDoor>();
			
			for (i = 0; i < numLockedDoors; i++ )
			{
				x = parser.lockedDoor[i].@x;
				y = parser.lockedDoor[i].@y;
				id = parser.lockedDoor[i].@id;
				var locked:Boolean; 
				parser.lockedDoor[i].@locked == "true" ? locked = true: locked = false;
				
				var ld:LockedDoor = new LockedDoor(x, y, id, locked);
				lockedDoors.add(ld);
				lockedDoorsList.push(ld);
			}
            
            // Add disappearing platform
            var numDisappearingPlatforms:int = parser.disappearingPlatform.length();
			disappearingPlatforms = new FlxGroup();
			disappearingPlatformsList = new Vector.<DisappearingPlatform>();
			
			for (i = 0; i < numDisappearingPlatforms; i++ )
			{
				x = parser.disappearingPlatform[i].@x;
				y = parser.disappearingPlatform[i].@y;
                w = parser.disappearingPlatform[i].@w;
                h = parser.disappearingPlatform[i].@h;
				id = parser.disappearingPlatform[i].@id;
				var visible:Boolean;
				parser.disappearingPlatform[i].@visible == 'true' ? visible = true : visible = false;
				
				var dp:DisappearingPlatform = new DisappearingPlatform(x, y, w, h, id, visible);
				disappearingPlatforms.add(dp);
				disappearingPlatformsList.push(dp);
			}
            
            // Add moving platforms
            var numMovingPlatforms:int = parser.movingPlatform.length();
			movingPlatforms = new FlxGroup();
			movingPlatformsList = new Vector.<MovingPlatform>();
			
			for (i = 0; i < numMovingPlatforms; i++ )
			{
				x = parser.movingPlatform[i].@x;
				y = parser.movingPlatform[i].@y;
                w = parser.movingPlatform[i].@w;
                h = parser.movingPlatform[i].@h;
				var xVelocity:Number = parser.movingPlatform[i].@xVel;
                var yVelocity:Number = parser.movingPlatform[i].@yVel;
                var distance:Number = parser.movingPlatform[i].@distance;
                id = parser.movingPlatform[i].@id;
                var moved:Boolean = parser.movingPlatform[i].@moved;
				var mp:MovingPlatform = new MovingPlatform(x, y, w, h, xVelocity, yVelocity, distance, id, moved);
				movingPlatforms.add(mp);
				movingPlatformsList.push(mp);
			}
            
            // Add text in game
			var numTexts:int = parser.text.length();
			texts = new FlxGroup();
			
			if ( !TitleScreen.contextTutorial )
			{
				for (i = 0; i < numTexts; i++)
				{
					x = parser.text[i].@x;
					y = parser.text[i].@y;
					w = parser.text[i].@w;
					var t:FlxText = new FlxText(x, y, w, parser.text[i], false);
					t.setFormat("Comic Sans MS", 11, 0x888888, "center", 0);
					
					texts.add(t);
				}
			}
			
			// Add balls in game (will eventually be partially or completely replaced
			// with ball-generators attached to switches)
            
			var numBalls:int = parser.ball.length();
			balls = new FlxGroup();
			
			for (i = 0; i < numBalls; i++ )
			{
				x = parser.ball[i].@x;
				y = parser.ball[i].@y;
				var r:int = parser.ball[i].@r;
				
				var b:Ball = new Ball(x, y, r);
				
				balls.add(b);
			}
			
			// Add ball launchers
			var numLaunchers:int = parser.launcher.length();
			ballLaunchers = new FlxGroup();
			
			for (i = 0; i < numLaunchers; i++ )
			{
				x = parser.launcher[i].@x;
				y = parser.launcher[i].@y;
				
				var xVel:Number = parser.launcher[i].@xVelocity;
				var yVel:Number = parser.launcher[i].@yVelocity;
				
				id = parser.launcher[i].@id;
				
				var bl:BallLauncher = new BallLauncher(x, y, xVel, yVel, id);
				
				ballLaunchers.add(bl);
			}
			
			// Add ball targets
			var numTargets:int = parser.target.length();
			ballTargets = new FlxGroup();
			
			for (i = 0; i < numTargets; i++ )
			{
				x = parser.target[i].@x;
				y = parser.target[i].@y;
				id = parser.target[i].@id;
				var rotate:Boolean = parser.target[i].@rotate == "true";
				
				var bt:BallTarget = new BallTarget(x, y, id, rotate);
				ballTargets.add(bt);
			}
			
			if ( TitleScreen.heatMapMode )
			{
				// Load heatMap.txt
				var textLoader:URLLoader = new URLLoader();
				
				textLoader.addEventListener(Event.COMPLETE, onHeatmapLoaded);
				textLoader.load(new URLRequest("heatMap.txt"));
			}
            
			// There should be exactly 1 player, start, and exit
			if( parser.player.length() != 1 )
			{
				trace("Error! There must be exactly 1 player tag in the level!");
				return;
			}
			player = new Player(parser.player.@x, parser.player.@y, this, parser.player.@c);
			
			if ( parser.start.length() != 1 )
			{
				trace("Error! There must be exactly 1 <start> tag in the level!");
				return;
			}
			startDoor = new Door(parser.start.@x, parser.start.@y, false);
			
			if ( parser.finish.length() != 1 )
			{
				trace("Error! There must be exactly 1 <finish> tag in the level!");
				return;
			}
			finishDoor = new Door(parser.finish.@x, parser.finish.@y, true);
			
			// Set up player collision group
			playerCollisionGroup = new FlxGroup();
			
            // Add all elements
			if ( startDoor )
				add(startDoor);
			if ( finishDoor )
				add(finishDoor);
			if ( player )
				add(player);
			if ( platforms )
			{
				add(platforms);
				playerCollisionGroup.add(platforms);
			}
            if ( lockedDoors )
			{
				add(lockedDoors);
				playerCollisionGroup.add(lockedDoors);
			}
            if ( switches )
				add(switches);
            if ( disappearingPlatforms )
			{
				add(disappearingPlatforms);
				playerCollisionGroup.add(disappearingPlatforms);
			}
            if ( movingPlatforms ) {
                add(movingPlatforms);
				playerCollisionGroup.add(movingPlatforms);
            }
			if ( inkPlatforms )
				add(inkPlatforms);
			if ( balls )
				add( balls );
			if ( ballLaunchers )
			{
				add( ballLaunchers );
				playerCollisionGroup.add(ballLaunchers);
			}
			if ( ballTargets )
				add( ballTargets );
				
			// Level Title
			title = new FlxText(200, 10, 140, "Level " + (levelNum + 1), false);
			title.setFormat("Comic Sans MS", 16, 0x000000, 'center', 0xF8F8F8);
			add(title);
				
			// Add Ink Count HUD
			inkCount = new FlxText(10, 10, 140, "Ink Left: ", false);
			inkCount.setFormat("Comic Sans MS", 16, 0x000000, 'left', 0xF8F8F8);
			add(inkCount);
			
			// Add Time HUD
			levelTime = new FlxText(10, 35, 200, "Time Elapsed: ", false);
			levelTime.setFormat("Comic Sans MS", 14, 0x000000, 'left', 0xF8F8F8);
			add(levelTime);
			
			// Add notification text zone
			notification = new FlxText(350, 200, 150, "", false)
			notification.setFormat("Comic Sans MS", 20, 0xff3333, "center", 0xF8F8F8);
			add(notification);
			
			// Add Ink Meter
			inkMeter = new FlxBar(0, 0, FlxBar.FILL_LEFT_TO_RIGHT, 50, 10, null, "Current Ink", 0, 90, true);
			inkMeter.setParent(player, "currInkLeft", true, -10, -10);
			inkMeter.createFilledBar(0xFFFFFFFF, 0xFF000000, true, 0xff000000);
			inkMeter.visible = false;
			add(inkMeter);
			
			// Add text
			if ( texts )
				add(texts);
			
			if (hardReset)
				elapsedTime = 0;
				logger.logLevelStart(levelNum + 1, {"Toggle Shift":TitleScreen.toggleShift,"Context Tutorials":TitleScreen.contextTutorial} );
				
			loaded = true;
            
            			
            // Add buttons to the top of the screen
            buttons = new Array();
            resetButton = new FlxButton(500, 8, "(R)estart", resetLevel);
			resetButton.loadGraphic(Resources.inGameButtonBG, false, false, 120, 40, false);
			resetButton.label.offset.y = -6;
			resetButton.label.setFormat(null, 11, 0xffffff, "center", 0);
			resetButton.label.width = 120;
            levelSelectButton = new FlxButton(350, 8, "Menu(ESC)", ReturnToLevelSelect);
			levelSelectButton.loadGraphic(Resources.inGameButtonBG, false, false, 120, 40, false);
			levelSelectButton.label.offset.y = -6;
			levelSelectButton.label.setFormat(null, 11, 0xffffff, "center", 0);
			levelSelectButton.label.width = 120;
			muteButton = new FlxButton(650, 8, "Mute", muteSound);
			muteButton.loadGraphic(Resources.inGameButtonBG, false, false, 120, 40, false);
			muteButton.label.offset.y = -6;
			muteButton.label.setFormat(null, 11, 0xffffff, "center", 0);
			muteButton.label.width = 120;
            add(resetButton);
            add(levelSelectButton);
			add(muteButton);
            buttons.push(resetButton);
            buttons.push(levelSelectButton);
			buttons.push(muteButton);
            
            FlxG.mouse.show();
		}
		        
        // Add Ink to level - Called from Player object
		/* 			See my comment in update() */
        public function addInk(X:Number, Y:Number):void {
            //var inkPoint:Platform = new Platform(X, Y, 1, 2, true);
			// New inkpath just like regular platforms
			inkMeter.visible = true;

			if ( drawingInk )
				curInk.addPoint(X, Y);
			else
			{
				drawingInk = true;
				curInk = new InkPlatform(X, Y);
				add(curInk);
			}

        }
		
		public function finishInk():void {
			if ( curInk )
			{
				logger.logAction(3, { "X":String(curInk.x),"Y":String(curInk.y),
										"W":String(curInk.width), "H":String(curInk.height) } );
													
				remove(curInk);
				inkMeter.visible = false;
				inkPlatforms.add(curInk);
				curInk.solidify();
			}
				
			drawingInk = false;
		}

		public function collideInkPlayer(actor:FlxObject, ink:FlxObject): Boolean {
			// This function will be called if an actor's bounding box
			// overlaps an ink platform's bounding box.
			// In this case we perform detailed collision with the
			// ink object.

			var ply:Player = Player(actor);
			
			var ip:InkPlatform = InkPlatform(ink);
			ip.collidePlayer(ply);
			return false;
		}
		
		public function collideInkBall(ball:FlxObject, ink:FlxObject): Boolean {
			// This function will be called if a ball's bounding box
			// overlaps an ink platform's bounding box.
			
			var b:Ball = ball as Ball;
			var ip:InkPlatform = ink as InkPlatform;
			ip.collideBall(b);
			return false;
		}
		
		public function collideFloorBall(ball:FlxObject, platform:FlxObject): Boolean {
			// TUNEVAL
			// This is the cutoff after which we decide the ball should stop bouncing
			// and come to rest on the ground.  Prevents the ball from getting stuck bouncing
			// up and down 1 pixel indefinitely.
			if ( ball.isTouching(FlxObject.FLOOR) && FlxU.abs(ball.velocity.y) < 20 )
			{
				ball.velocity.y = 0.0;
			}
			return false;
		}
		
		public function targetNotify(ball:FlxObject, target:FlxObject): Boolean {
			var bt:BallTarget = target as BallTarget;
			
			var id:int = bt.getId();
			
			for each(var lockedDoor:LockedDoor in lockedDoorsList) {
				// Unlock the door associated with the switch
				if (lockedDoor.getId() == id) {
					lockedDoor.toggleLock();
					lockedDoor.solid = lockedDoor.isLocked();
				}
			}
			for each(var dp:DisappearingPlatform in disappearingPlatformsList) {
				// Unlock the door associated with the switch
				if (dp && dp.getId() == id) {
					dp.toggleVisible();
				}
			}
			for each(var bl:BallLauncher in ballLaunchers.members) {
				// Launch a ball
				if (bl && bl.getId() == id) {
					bl.launch(this);
				}
			}
			
			(ball as Ball).death();
			//removeBall(ball as Ball);
						
			return false;
		}
		
		public function addBall(ball:Ball) : void
		{
			if ( !balls )
			{
				balls = new FlxGroup();
				add(balls);
			}
			
			balls.add(ball);
		}
		
		public function removeBall(ball:Ball) : void
		{
			if ( !balls )
				return;
			
			balls.remove(ball);
		}
		
		// Remove all objects in level and recreate when reseting
		public function resetLevel() : void
		{	
			resetCount += 1;

			numBubbles = 0;
			
			remove(platforms);
			remove(inkPlatforms);
			if (curInk) {
				remove(curInk)
				drawingInk = false;
			}	
			inkPlatforms.clear();
			platforms.clear();
			lockedDoors.clear();
			disappearingPlatforms.clear();
			
			remove(inkMeter);
			remove(player);
			remove(startDoor);
			remove(finishDoor);
			remove(inkCount);
			remove(levelTime);
			remove(title);
			remove(notification);
			remove(balls);
			remove(ballLaunchers);
			remove(ballTargets);
            
            // Remove switches and doors
            remove(switches);
            remove(lockedDoors);
            remove(disappearingPlatforms);
            remove(movingPlatforms);
			
			loaded = false;
			
			loadLevel(levelInfo, false);
		}
        
        private function ReturnToLevelSelect():void {
            var ls:LevelSelect = new LevelSelect(soundtrack);
            ls.setGameProgress(gameInfo);
            FlxG.switchState(ls);
        }
		
		private function muteSound() : void {
			if ( soundtrack )
			{
				if ( soundtrack.volume != 0 )
					soundtrack.volume = 0;
				else
					soundtrack.volume = 1;
			}
		}
		
		private function onHeatmapLoaded(e:Event):void {
			var lines:Array = e.target.data.split(';');
			
			heatMapRects = new FlxGroup();
			add(heatMapRects);
			
			for each (var str:String in lines)
			{
				var levelEnd:int = str.indexOf("-");
				var level:int = int(str.substring(0, levelEnd));
				
				if ( level != levelNum+1 )
					continue;
				
				var xBegin:int = str.indexOf("{");
				var xEnd:int = str.indexOf("}", xBegin);
				
				var xArry:Array = str.substring(xBegin + 1, xEnd).split(",");
				
				var yBegin:int = str.indexOf("{",xEnd);
				var yEnd:int = str.indexOf("}", yBegin);
				
				var yArry:Array = str.substring(yBegin + 1, yEnd).split(",");
				
				var wBegin:int = str.indexOf("{", yEnd);
				var wEnd:int = str.indexOf("}", wBegin);
				
				var wArry:Array = str.substring(wBegin + 1, wEnd).split(",");
				
				var hBegin:int = str.indexOf("{", wEnd);
				var hEnd:int = str.indexOf("}", hBegin);
				
				var hArry:Array = str.substring(hBegin + 1, hEnd).split(",");
				
				for ( var i:int = 0; i < xArry.length; i++ )
				{
					var hmp:HeatMapRect = new HeatMapRect(2*xArry[i], 2*yArry[i], 2*wArry[i], 2*hArry[i]);
					heatMapRects.add(hmp);
				}
			}
		}
		
		// Check to see if the player is in a tutorial zone in the current level
		private function updateTutorialBubbles() : void {
			if ( !TitleScreen.contextTutorial )
				return;
			
			// Sloppy implementation but its what it is
			// What it is is easy to A/B test
			// Chyeaah!
			switch(levelNum+1)
			{
				case 0:
					if ( numBubbles == 0 )
					{
						if ( !player.isSpeechUp() )
							player.setSpeech(player.BUBBLE_RIGHT, 30);
						if ( (FlxG.keys.justPressed("D") || FlxG.keys.justPressed("RIGHT")) )
						{
							player.clearSpeech();
							numBubbles++;
						}
					}
					else if ( numBubbles == 1 )
					{
						if ( !player.isSpeechUp() && player.x > 75 )
							player.setSpeech(player.BUBBLE_LEFT, 30);
						if ( (FlxG.keys.justPressed("A") || FlxG.keys.justPressed("LEFT")) )
						{
							player.clearSpeech();
							numBubbles++;
						}
					}
					else if ( numBubbles == 2 )
					{
						if ( !player.isSpeechUp() && player.x < 65 )
							player.setSpeech(player.BUBBLE_UP, 30);
						if ( (FlxG.keys.justPressed("W") || FlxG.keys.justPressed("UP")) )
						{
							player.clearSpeech();
							numBubbles++;
						}
					}
					else if (numBubbles == 3 )
					{
						if ( !player.isSpeechUp() && player.jumpStatus == player.GROUND )
						{
							player.setSpeech(player.BUBBLE_EXIT, 5);
							numBubbles++;
						}
					}
					break;
				case 1:
					if ( numBubbles == 0 )
					{
						if ( !player.isSpeechUp() )
							player.setSpeech(player.BUBBLE_RIGHT, 30);
						if ( player.velocity.x == player.maxVelocity.x && player.x > 75 )
						{
							player.clearSpeech();
							numBubbles++;
						}
					}
					else if ( numBubbles == 1 )
					{
						if ( !player.isSpeechUp() && player.velocity.x == player.maxVelocity.x )
							player.setSpeech(player.BUBBLE_UP, 30);
						if ( (FlxG.keys.justPressed("W") || FlxG.keys.justPressed("UP")) )
						{
							player.clearSpeech();
							numBubbles++;
						}
					}
					else if ( numBubbles == 2 )
					{
						if ( !player.isSpeechUp() && player.jumpStatus != player.GROUND )
							player.setSpeech(player.BUBBLE_SHIFT, 2);
						if ( FlxG.keys.justPressed("SHIFT") )
						{
							player.clearSpeech();
							numBubbles++;
						}
						else if ( !player.isSpeechUp() ) // Player didn't successfully make arc
						{
							numBubbles = 4;
						}
					}
					else if (numBubbles == 3 && !TitleScreen.toggleShift )
					{
						if ( !player.isSpeechUp() && FlxG.keys.pressed("SHIFT") )
							player.setSpeech(player.BUBBLE_SHIFT_DOWN, 4);
						if ( player.jumpStatus == player.GROUND )
						{
							if ( inkPlatforms.members.length > 0 || curInk != null )
							{
								numBubbles = 5;
								player.clearSpeech();
							}
							else
							{
								player.clearSpeech();
								numBubbles++;
							}
						}
					}
					else if (numBubbles == 4 )
					{
						if ( !player.isSpeechUp() && player.x > 75 )
							player.setSpeech(player.BUBBLE_LEFT, 10);
						if (player.x < 75 )
						{
							player.clearSpeech();
							numBubbles = 0;
						}
					}
					else if (numBubbles == 5 )
					{
						if ( !player.isSpeechUp() )
						{
							player.setSpeech(player.BUBBLE_INK, 4);
							if ( (curInk && curInk.playerPresent) || 
									(inkPlatforms.members.length > 0 && 
									(inkPlatforms.members[0] as InkPlatform).playerPresent ) )
								numBubbles++;
						}
					}
					else if (numBubbles == 6 )
					{
						if ( !player.isSpeechUp() && player.jumpStatus == player.GROUND )
						{
							player.setSpeech(player.BUBBLE_EXIT, 30);
							numBubbles++;
						}
					}
					break;
				case 2:
					if ( numBubbles == 0 && TitleScreen.toggleShift)
					{
						if ( player.jumpStatus != player.GROUND && player.isSpeechUp() &&
							FlxG.keys.justPressed("SHIFT"))
						{
							player.clearSpeech();
							numBubbles++;
							break;
						}
						
						if ( !player.isSpeechUp() && drawingInk &&
								player.jumpStatus != player.GROUND )
							player.setSpeech(player.BUBBLE_SHIFT, 2);
							
					}
					break;
				case 3:
					if ( numBubbles == 0 )
					{	
						if ( !player.isSpeechUp() )
							player.setSpeech(player.BUBBLE_SWITCH, 30);
						if ( player.overlaps( switchesList[0] ) )
						{
							player.clearSpeech();
							numBubbles++;
						}
					}
					else if ( numBubbles == 1 )
					{
						if ( !player.isSpeechUp() )
							player.setSpeech(player.BUBBLE_DOWN, 30);
						if ( switchesList[0].isOn() )
						{
							player.clearSpeech();
							numBubbles++;
						}
						if ( !player.overlaps( switchesList[0] ) )
						{
							player.clearSpeech();
							numBubbles = 0;
						}
					}
					break;
				case 8:
					if ( numBubbles == 0 )
					{	
						if ( !player.isSpeechUp() )
							player.setSpeech(player.BUBBLE_SWITCH, 30);
						if ( player.overlaps( switchesList[0] ) )
						{
							player.clearSpeech();
							numBubbles++;
						}
					}
					break;
				default:
					break;
			}
		}
    }
}